{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from web3 import Web3\n",
    "from eth_typing import HexStr\n",
    "import solcx\n",
    "from solcx import compile_source\n",
    "import pickle\n",
    "import base64\n",
    "import time\n",
    "from tqdm import trange\n",
    "from Crypto.Cipher import PKCS1_OAEP\n",
    "from Crypto.PublicKey import RSA\n",
    "from cryptography import x509\n",
    "from cryptography.hazmat.primitives import hashes, serialization\n",
    "from cryptography.hazmat.primitives.asymmetric import padding\n",
    "from cryptography import x509\n",
    "from cryptography.hazmat.primitives import hashes, serialization\n",
    "from cryptography.hazmat.primitives.asymmetric import padding\n",
    "from OpenSSL.crypto import load_certificate, X509Store, X509StoreContext, X509StoreContextError, FILETYPE_PEM"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Prepare the Ethereum environment ###"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>1.Connect to the private Ethereum node</i></b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "w3 = Web3(Web3.IPCProvider('data/geth.ipc'))\n",
    "w3.isConnected()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>2.List the current accounts and balance</i></b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Oracle account, address:0xbDfa9d9C5656a2e4f09d53768DA17044AfE4F4d7, balance:7449.008570308990301 ether\n",
      "Vehicle account, address:0x3312521D5892D45Eb23b38c95375D9376DCA8e0B, balance:107.99296777592 ether\n",
      "Consumer account, address:0x393B9AeAb28Aa2dD4Faf840a00F0787A9b7A0519, balance:76.998461915 ether\n"
     ]
    }
   ],
   "source": [
    "account_name = ['Oracle account', 'Vehicle account', 'Consumer account']\n",
    "for i in range(3):\n",
    "    account = w3.eth.accounts[i]\n",
    "    print(\"{}, address:{}, balance:{} ether\".format(account_name[i], account, Web3.fromWei(w3.eth.get_balance(account), 'ether')))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>3.Start mining</i></b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "w3.geth.miner.start(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Deploy the Oracle contract ###"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>1.Compile the Oracle contract<i></b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def compile_source_file(file_path):\n",
    "    with open(file_path, 'r') as f:\n",
    "        source = f.read()\n",
    "    return compile_source(source)\n",
    " \n",
    "def deploy_contract(address, contract_interface, **kwargs):\n",
    "    contract = w3.eth.contract(\n",
    "        address=address,\n",
    "        abi=contract_interface['abi'],\n",
    "        bytecode=contract_interface['bin']).constructor(**kwargs).transact()\n",
    "    return contract\n",
    "\n",
    "oracle_compiled_sol = compile_source_file('oracle.sol')\n",
    "oracle_contract_id, oracle_contract_interface = oracle_compiled_sol.popitem()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>2.Deploy the Oracle contract to Ethereum</i></b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Oracle contract deployed\n",
      "transaction hash:0x66c8c3b9dffde7014b872d295b43c1e7da05c11852af805498ef8fa6c633345d\n"
     ]
    }
   ],
   "source": [
    "w3.eth.default_account=w3.eth.accounts[0]\n",
    "w3.geth.personal.unlockAccount(w3.eth.accounts[0], 'abc12345')\n",
    "tx_hash = deploy_contract(w3.eth.accounts[0], oracle_contract_interface)\n",
    "print('Oracle contract deployed\\ntransaction hash:{}'.format(Web3.toHex(tx_hash)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>3.Query the Oracle contract address</i></b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Oracle contract address:0x366A0d5be58b686EA4af1182111915F3e7FD4b76\n"
     ]
    }
   ],
   "source": [
    "oracle_contract_address = w3.eth.get_transaction_receipt(tx_hash)['contractAddress']\n",
    "print('Oracle contract address:{}'.format(oracle_contract_address))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>4.Run the Oracle app to handle the oracle contract events</i></b>\n",
    "<br>\n",
    "Modify the oracle contract address in the oracle.py and then run, it will continuously monitor the oracle contract events and perform the data decryption and encryption."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Vehicle public the data ###"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>1. Upload the encrypted data file to the IPFS</i></b><br>\n",
    "IPFS stands for InterPlanetary File System, a distruibuted and de-centralized file storage system.<br>\n",
    "After file upload it will return a Hash, it can later get the file by use the command ipfs get hash.<br>\n",
    "The test.zip file includes the vehicle mesaurement data and encrypted with password 'abcde123456'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\r",
      " 591 B / 591 B [=======================================================] 100.00%\u001b[2K\r",
      "added QmSpBSx5fRtHdV4N2MCY5HdhjG6jyPCGVwCvxktjB4oHrT test.zip\r\n",
      "\r",
      " 591 B / 591 B [=======================================================] 100.00%"
     ]
    }
   ],
   "source": [
    "!ipfs add test.zip"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>2.Encrypt the data file password using the Oracle public key</i></b><br>\n",
    "As the password store in the contract is visible to all user, need to encrypt first."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "iKh1Bx2VwpLs73gVuDSHXzVPKyETVVmWSAH4EhwteEId1X0OYqvAQWtKvNVmW68684qxsy7w3gk7b2hkcHt8XvsjDHugoMADFaQdChptmXY5wtluPVg6MV+Gj8+wYy8HSjF/BzfPWvh9QKEG1KGe2g8D3A6IGN0BTXYB0qMNKZk=\n"
     ]
    }
   ],
   "source": [
    "password = b'abcde123456'   #The test.zip file password\n",
    "with open('oracle_pubkey.pem', 'r') as f:\n",
    "    public_pem = f.read()\n",
    "pub_key = RSA.import_key(public_pem)\n",
    "cipher = PKCS1_OAEP.new(key=pub_key)\n",
    "cipher_text = base64.b64encode(cipher.encrypt(password)).decode('ascii')\n",
    "print(cipher_text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>3.Compile and deploy the contract</i></b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Vehicle contract deployed, transaction hash:0x181e1b49bdd506d633c73051a14c03b73f5b1682284adcc5a5cb0bc8735c60ab\n"
     ]
    }
   ],
   "source": [
    "compiled_sol = compile_source_file('getpass.sol')\n",
    "contract_id, contract_interface = compiled_sol.popitem()\n",
    "w3.eth.default_account=w3.eth.accounts[1]\n",
    "w3.geth.personal.unlockAccount(w3.eth.accounts[1], 'abc12345')\n",
    "contract_args = {\n",
    "    '_oracle_address':oracle_contract_address,\n",
    "    '_encrypted_password':cipher_text,\n",
    "    '_amount':Web3.toWei(1, 'ether')\n",
    "}\n",
    "vehicle_tx_hash = deploy_contract(w3.eth.accounts[1], contract_interface, **contract_args)\n",
    "print('Vehicle contract deployed, transaction hash:{}'.format(Web3.toHex(vehicle_tx_hash)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>4.Query the contract address</i></b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Vehicle contract address:0xE276b02FF2e388ca6e1e779C79d8d3C7001C06f0\n"
     ]
    }
   ],
   "source": [
    "contract_address = w3.eth.get_transaction_receipt(vehicle_tx_hash)['contractAddress']\n",
    "print('Vehicle contract address:{}'.format(contract_address))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>5.Generate the metadata to describe the data file content.</i></b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "metadata = {}\n",
    "metadata['Type'] = 'Vehicle Data'\n",
    "metadata['VIN'] = str(abs(hash('Vehicle_0123456')))\n",
    "metadata['Location'] = 'Guangzhou'\n",
    "metadata['Date_from'] = '2021-09-23 08:00'\n",
    "metadata['Date_to'] = '2021-09-24 00:00'\n",
    "metadata['Record_num'] = 2312\n",
    "metadata['Contract_address'] = contract_address\n",
    "metadata['Price'] = 1.0\n",
    "metadata['File'] = 'QmSpBSx5fRtHdV4N2MCY5HdhjG6jyPCGVwCvxktjB4oHrT'\n",
    "metadata['Description'] = ['speed,gps,sensor,accident,network']\n",
    "metadata['Cert'] = open('client.crt', 'r').read()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>6.Load the device private key to sign the metadata</i></b><br>\n",
    "Ensure the data are published by a trustable device and not get modified by others."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open(\"clientkey.pem\", \"rb\") as key_file:\n",
    "    private_key = serialization.load_pem_private_key(key_file.read(),password=None)\n",
    "signature = private_key.sign(\n",
    "    pickle.dumps(metadata),\n",
    "    padding.PSS(\n",
    "        mgf=padding.MGF1(hashes.SHA256()),\n",
    "        salt_length=padding.PSS.MAX_LENGTH\n",
    "    ),\n",
    "    hashes.SHA256()\n",
    ")\n",
    "metadata['Signature'] = signature"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>7.Create the transaction, add a special flag 0xb923249d in the input data field to identify the transaction</i></b><br>\n",
    "The flag is the first 4 bytes of the Keccak-256 hash of text \"Test Flag\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Transaction hash:0x1f3b5e188c23b952583fba4fcf3ff14c6650ef2c1c3ff15985357f397520846f\n"
     ]
    }
   ],
   "source": [
    "transaction_flag = Web3.keccak(text='Test Flag')[:4]\n",
    "m = transaction_flag + pickle.dumps(metadata)\n",
    "w3.geth.personal.unlockAccount(w3.eth.accounts[1], 'abc12345')\n",
    "tx_hash = w3.eth.sendTransaction({'from':w3.eth.accounts[1],'to':w3.eth.accounts[1],'value':Web3.toWei(0, 'ether'), 'data':m})\n",
    "print('Transaction hash:{}'.format(Web3.toHex(tx_hash)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>8.Get the transaction detail</i></b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AttributeDict({'blockHash': HexBytes('0xae72bbefe072080da1fa5da22e28256f8b28114fece2b7b075e5f91b030c114f'),\n",
       " 'blockNumber': 3842,\n",
       " 'from': '0x3312521D5892D45Eb23b38c95375D9376DCA8e0B',\n",
       " 'gas': 244080,\n",
       " 'gasPrice': 1000000000,\n",
       " 'hash': HexBytes('0x1f3b5e188c23b952583fba4fcf3ff14c6650ef2c1c3ff15985357f397520846f'),\n",
       " 'input': '0xb923249d80049513070000000000007d94288c0454797065948c0c56656869636c652044617461948c0356494e948c1332303739303931373533383531393437383636948c084c6f636174696f6e948c094775616e677a686f75948c09446174655f66726f6d948c10323032312d30392d32332030383a3030948c07446174655f746f948c10323032312d30392d32342030303a3030948c0a5265636f72645f6e756d944d08098c10436f6e74726163745f61646472657373948c2a307845323736623032464632653338386361366531653737394337396438643343373030314330366630948c05507269636594473ff00000000000008c0446696c65948c2e516d537042537835665274486456344e324d4359354864686a47366a7950434756774376786b746a42346f487254948c0b4465736372697074696f6e945d948c2173706565642c6770732c73656e736f722c6163636964656e742c6e6574776f726b94618c044365727494589c0400002d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949444f7a434341694d4346423371597842596f58576a4855414466746e5a6570587a474637774d413047435371475349623344514542437755414d466f780a437a414a42674e5642415954416b4e4f4d517377435159445651514944414a485244454c4d416b47413155454277774352316f78447a414e42674e5642416f4d0a426c4a7662335244515445504d4130474131554543777747556d397664454e424d513877445159445651514444415a5362323930513045774868634e4d6a45770a4f5449324d5449794f4455795768634e4d7a45774f5449304d5449794f445579576a42614d517377435159445651514745774a44546a454c4d416b47413155450a4341774352305178437a414a42674e564241634d416b64614d517777436759445651514b44414e43545663784544414f42674e564241734d42315a6c61476c6a0a624755784554415042674e5642414d4d43465a6c61476c6a624755784d494942496a414e42676b71686b6947397730424151454641414f43415138414d4949420a43674b4341514541757439664165396579674a7a774c6f58596339374a6263434a4830594d6e62742b46774271343652644666634a3941496457346e74796d6b0a6673546473436467476b7141463358587538306c4769377a67437675365064455a7563465444647370535a41546f6c666c58454653682b46537859312b5035690a547a584a7a534456567539453735426d506133375134756d4b4171537935726137364862776b33476c48573434695744526674595a62395a4d7433715a4559480a59796f2f556d454e5a3772344b456a7757776f6d30424470797067756335333343345977506f66626f4d516f76546e7969584c51566e7a62576d7736317958340a5436625a67574d594b64744b425a6971586f636c3135596245434a45426c66414c456b78547a30697a49494f4367797773314b2f5a774b304e57506d525676620a7a5871587047426c6a6f7a4952494757537a546a4354797a3935586b74514944415141424d4130474353714753496233445145424377554141344942415142410a7a2f6830524d3246353649644a4e68493456734758686c71684b4d564c4d74526370427937664370344274714d73462b2f4b6175676c446f6d77372b326570360a646f73495a31444d623752614d41466656556e4f327259464c6e484650525867613667382f54794852374a5531525a59374b58305338495670346632485853440a4467682f634f6478474458756d3261554f426f52316d374259676b47337257536c747653772b4f4461516f49473859436f2b4b544d52764876756a784f3459530a616345553358457852662f6a347a5975536c74764776594b7171704d47473335656d5a36526e5a5a6e54625a5255424b7342572f64732b7a59775936716233370a752b5737377846433555476238376538655842424567362b6e5850624f425338412b614c355735314c63584d31546c31595a5461396c5778556e4938793158560a5752426e6951673055725041363031776851334e0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a948c095369676e6174757265944200010000231d7ef3521cfe7bf9b0915756074f8abbfdbb05b4c112dec9d68fb0f5df6bb2759a1fef5a87b963bff1f4a8fc9037391393665bf95809b45239d2d1b4458fd43ed1969a6da391c868b5b6cb245222f6e9046399b886cf43639bb36d5f5c87ea4f44b304a61c4d578f394343f640b7284ac20b3576f512e3f2369c3f50cde62b931fdeae6cc908d9e79a9d9045dc03a37eaa8d98d31689e5a637f53ee921fc6240ca484b8a255816558a46aef83a6daaca138d0d8150cc6e3b707ea51e225222ed5752f73a96d4d2360a4093e412217a07e7a79c77aed376ddddda14aa72de730451971a6cb832a86ede2723bfcb0cf9df392b02bd223a888bc168845334b0b194752e',\n",
       " 'nonce': 31,\n",
       " 'to': '0x3312521D5892D45Eb23b38c95375D9376DCA8e0B',\n",
       " 'transactionIndex': 0,\n",
       " 'value': 0,\n",
       " 'type': '0x0',\n",
       " 'v': 65,\n",
       " 'r': HexBytes('0x9ba78dd53191ac46a24255feace3699cda13a82a701832181df5319f4ac2b35e'),\n",
       " 's': HexBytes('0x716950735eeba54d1279913a593b0e9ecfb286f13155254282fb6b09a1d3c8f3')})"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "w3.eth.get_transaction(tx_hash)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Data consumer find the data file and trade ###"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>1.Scan the blocks to find the interested transactions</i></b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 3845/3845 [00:06<00:00, 581.69it/s]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total transactions found:4\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "tx_list = []\n",
    "transaction_flag = Web3.keccak(text='Test Flag')[:4].hex()\n",
    "for block in trange(w3.eth.get_block_number()+1):\n",
    "    transactions = w3.eth.get_block(block_identifier=block, full_transactions=True)['transactions']\n",
    "    for tx in transactions:\n",
    "        if tx['input'][:10] == transaction_flag:\n",
    "            tx_list.append(tx)\n",
    "print('Total transactions found:{}'.format(str(len(tx_list))))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>2.View the latest transaction</i></b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AttributeDict({'blockHash': HexBytes('0xae72bbefe072080da1fa5da22e28256f8b28114fece2b7b075e5f91b030c114f'),\n",
       " 'blockNumber': 3842,\n",
       " 'from': '0x3312521D5892D45Eb23b38c95375D9376DCA8e0B',\n",
       " 'gas': 244080,\n",
       " 'gasPrice': 1000000000,\n",
       " 'hash': HexBytes('0x1f3b5e188c23b952583fba4fcf3ff14c6650ef2c1c3ff15985357f397520846f'),\n",
       " 'input': '0xb923249d80049513070000000000007d94288c0454797065948c0c56656869636c652044617461948c0356494e948c1332303739303931373533383531393437383636948c084c6f636174696f6e948c094775616e677a686f75948c09446174655f66726f6d948c10323032312d30392d32332030383a3030948c07446174655f746f948c10323032312d30392d32342030303a3030948c0a5265636f72645f6e756d944d08098c10436f6e74726163745f61646472657373948c2a307845323736623032464632653338386361366531653737394337396438643343373030314330366630948c05507269636594473ff00000000000008c0446696c65948c2e516d537042537835665274486456344e324d4359354864686a47366a7950434756774376786b746a42346f487254948c0b4465736372697074696f6e945d948c2173706565642c6770732c73656e736f722c6163636964656e742c6e6574776f726b94618c044365727494589c0400002d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949444f7a434341694d4346423371597842596f58576a4855414466746e5a6570587a474637774d413047435371475349623344514542437755414d466f780a437a414a42674e5642415954416b4e4f4d517377435159445651514944414a485244454c4d416b47413155454277774352316f78447a414e42674e5642416f4d0a426c4a7662335244515445504d4130474131554543777747556d397664454e424d513877445159445651514444415a5362323930513045774868634e4d6a45770a4f5449324d5449794f4455795768634e4d7a45774f5449304d5449794f445579576a42614d517377435159445651514745774a44546a454c4d416b47413155450a4341774352305178437a414a42674e564241634d416b64614d517777436759445651514b44414e43545663784544414f42674e564241734d42315a6c61476c6a0a624755784554415042674e5642414d4d43465a6c61476c6a624755784d494942496a414e42676b71686b6947397730424151454641414f43415138414d4949420a43674b4341514541757439664165396579674a7a774c6f58596339374a6263434a4830594d6e62742b46774271343652644666634a3941496457346e74796d6b0a6673546473436467476b7141463358587538306c4769377a67437675365064455a7563465444647370535a41546f6c666c58454653682b46537859312b5035690a547a584a7a534456567539453735426d506133375134756d4b4171537935726137364862776b33476c48573434695744526674595a62395a4d7433715a4559480a59796f2f556d454e5a3772344b456a7757776f6d30424470797067756335333343345977506f66626f4d516f76546e7969584c51566e7a62576d7736317958340a5436625a67574d594b64744b425a6971586f636c3135596245434a45426c66414c456b78547a30697a49494f4367797773314b2f5a774b304e57506d525676620a7a5871587047426c6a6f7a4952494757537a546a4354797a3935586b74514944415141424d4130474353714753496233445145424377554141344942415142410a7a2f6830524d3246353649644a4e68493456734758686c71684b4d564c4d74526370427937664370344274714d73462b2f4b6175676c446f6d77372b326570360a646f73495a31444d623752614d41466656556e4f327259464c6e484650525867613667382f54794852374a5531525a59374b58305338495670346632485853440a4467682f634f6478474458756d3261554f426f52316d374259676b47337257536c747653772b4f4461516f49473859436f2b4b544d52764876756a784f3459530a616345553358457852662f6a347a5975536c74764776594b7171704d47473335656d5a36526e5a5a6e54625a5255424b7342572f64732b7a59775936716233370a752b5737377846433555476238376538655842424567362b6e5850624f425338412b614c355735314c63584d31546c31595a5461396c5778556e4938793158560a5752426e6951673055725041363031776851334e0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a948c095369676e6174757265944200010000231d7ef3521cfe7bf9b0915756074f8abbfdbb05b4c112dec9d68fb0f5df6bb2759a1fef5a87b963bff1f4a8fc9037391393665bf95809b45239d2d1b4458fd43ed1969a6da391c868b5b6cb245222f6e9046399b886cf43639bb36d5f5c87ea4f44b304a61c4d578f394343f640b7284ac20b3576f512e3f2369c3f50cde62b931fdeae6cc908d9e79a9d9045dc03a37eaa8d98d31689e5a637f53ee921fc6240ca484b8a255816558a46aef83a6daaca138d0d8150cc6e3b707ea51e225222ed5752f73a96d4d2360a4093e412217a07e7a79c77aed376ddddda14aa72de730451971a6cb832a86ede2723bfcb0cf9df392b02bd223a888bc168845334b0b194752e',\n",
       " 'nonce': 31,\n",
       " 'to': '0x3312521D5892D45Eb23b38c95375D9376DCA8e0B',\n",
       " 'transactionIndex': 0,\n",
       " 'value': 0,\n",
       " 'type': '0x0',\n",
       " 'v': 65,\n",
       " 'r': HexBytes('0x9ba78dd53191ac46a24255feace3699cda13a82a701832181df5319f4ac2b35e'),\n",
       " 's': HexBytes('0x716950735eeba54d1279913a593b0e9ecfb286f13155254282fb6b09a1d3c8f3')})"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tx_list[-1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>3.Extract the metadata from the transaction 'input' field</i></b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'Type': 'Vehicle Data',\n",
       " 'VIN': '2079091753851947866',\n",
       " 'Location': 'Guangzhou',\n",
       " 'Date_from': '2021-09-23 08:00',\n",
       " 'Date_to': '2021-09-24 00:00',\n",
       " 'Record_num': 2312,\n",
       " 'Contract_address': '0xE276b02FF2e388ca6e1e779C79d8d3C7001C06f0',\n",
       " 'Price': 1.0,\n",
       " 'File': 'QmSpBSx5fRtHdV4N2MCY5HdhjG6jyPCGVwCvxktjB4oHrT',\n",
       " 'Description': ['speed,gps,sensor,accident,network'],\n",
       " 'Cert': '-----BEGIN CERTIFICATE-----\\nMIIDOzCCAiMCFB3qYxBYoXWjHUADftnZepXzGF7wMA0GCSqGSIb3DQEBCwUAMFox\\nCzAJBgNVBAYTAkNOMQswCQYDVQQIDAJHRDELMAkGA1UEBwwCR1oxDzANBgNVBAoM\\nBlJvb3RDQTEPMA0GA1UECwwGUm9vdENBMQ8wDQYDVQQDDAZSb290Q0EwHhcNMjEw\\nOTI2MTIyODUyWhcNMzEwOTI0MTIyODUyWjBaMQswCQYDVQQGEwJDTjELMAkGA1UE\\nCAwCR0QxCzAJBgNVBAcMAkdaMQwwCgYDVQQKDANCTVcxEDAOBgNVBAsMB1ZlaGlj\\nbGUxETAPBgNVBAMMCFZlaGljbGUxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\\nCgKCAQEAut9fAe9eygJzwLoXYc97JbcCJH0YMnbt+FwBq46RdFfcJ9AIdW4ntymk\\nfsTdsCdgGkqAF3XXu80lGi7zgCvu6PdEZucFTDdspSZATolflXEFSh+FSxY1+P5i\\nTzXJzSDVVu9E75BmPa37Q4umKAqSy5ra76Hbwk3GlHW44iWDRftYZb9ZMt3qZEYH\\nYyo/UmENZ7r4KEjwWwom0BDpypguc533C4YwPofboMQovTnyiXLQVnzbWmw61yX4\\nT6bZgWMYKdtKBZiqXocl15YbECJEBlfALEkxTz0izIIOCgyws1K/ZwK0NWPmRVvb\\nzXqXpGBljozIRIGWSzTjCTyz95XktQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBA\\nz/h0RM2F56IdJNhI4VsGXhlqhKMVLMtRcpBy7fCp4BtqMsF+/KauglDomw7+2ep6\\ndosIZ1DMb7RaMAFfVUnO2rYFLnHFPRXga6g8/TyHR7JU1RZY7KX0S8IVp4f2HXSD\\nDgh/cOdxGDXum2aUOBoR1m7BYgkG3rWSltvSw+ODaQoIG8YCo+KTMRvHvujxO4YS\\nacEU3XExRf/j4zYuSltvGvYKqqpMGG35emZ6RnZZnTbZRUBKsBW/ds+zYwY6qb37\\nu+W77xFC5UGb87e8eXBBEg6+nXPbOBS8A+aL5W51LcXM1Tl1YZTa9lWxUnI8y1XV\\nWRBniQg0UrPA601whQ3N\\n-----END CERTIFICATE-----\\n',\n",
       " 'Signature': b'#\\x1d~\\xf3R\\x1c\\xfe{\\xf9\\xb0\\x91WV\\x07O\\x8a\\xbb\\xfd\\xbb\\x05\\xb4\\xc1\\x12\\xde\\xc9\\xd6\\x8f\\xb0\\xf5\\xdfk\\xb2u\\x9a\\x1f\\xefZ\\x87\\xb9c\\xbf\\xf1\\xf4\\xa8\\xfc\\x9079\\x13\\x93f[\\xf9X\\t\\xb4R9\\xd2\\xd1\\xb4E\\x8f\\xd4>\\xd1\\x96\\x9am\\xa3\\x91\\xc8h\\xb5\\xb6\\xcb$R\"\\xf6\\xe9\\x04c\\x99\\xb8\\x86\\xcfCc\\x9b\\xb3m_\\\\\\x87\\xeaOD\\xb3\\x04\\xa6\\x1cMW\\x8f9CC\\xf6@\\xb7(J\\xc2\\x0b5v\\xf5\\x12\\xe3\\xf26\\x9c?P\\xcd\\xe6+\\x93\\x1f\\xde\\xael\\xc9\\x08\\xd9\\xe7\\x9a\\x9d\\x90E\\xdc\\x03\\xa3~\\xaa\\x8d\\x98\\xd3\\x16\\x89\\xe5\\xa67\\xf5>\\xe9!\\xfcb@\\xcaHK\\x8a%X\\x16U\\x8aF\\xae\\xf8:m\\xaa\\xca\\x13\\x8d\\r\\x81P\\xccn;p~\\xa5\\x1e\"R\"\\xedWR\\xf7:\\x96\\xd4\\xd26\\n@\\x93\\xe4\\x12!z\\x07\\xe7\\xa7\\x9cw\\xae\\xd3v\\xdd\\xdd\\xda\\x14\\xaar\\xdes\\x04Q\\x97\\x1al\\xb82\\xa8n\\xde\\'#\\xbf\\xcb\\x0c\\xf9\\xdf9+\\x02\\xbd\":\\x88\\x8b\\xc1h\\x84S4\\xb0\\xb1'}"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "metadata = pickle.loads(Web3.toBytes(hexstr=tx_list[-1]['input'][10:]))\n",
    "metadata"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>4.Check if any rating exist relate to the data file</i></b><br>\n",
    "The history rating was recorded as contract events. The event topic of the rating is the Kecaak-256 hash of LogRating((address,uint256)). <br>Only the account that previous buy the data file could give rating."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = w3.eth.filter({'fromBlock': 0, 'toBlock': 'latest'})\n",
    "entries = f.get_all_entries()\n",
    "ratings = []\n",
    "for entry in entries:\n",
    "    if entry['address']==metadata['Contract_address'] and entry['topics'][0]==Web3.keccak(text='LogRating((address,uint256))'):\n",
    "        data = entry['data'][2:]\n",
    "        buyer_addr = Web3.toHex(hexstr=data[24:64])\n",
    "        score = Web3.toInt(hexstr=data[64:128])\n",
    "        ratings.append((buyer_addr, score))\n",
    "        print(\"Buyer address:{}, rating score:{}\".format(buyer_addr, score))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>5.Verify the data is generated by the trusted device and not modified by others</i></b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Device certificate verify pass\n",
      "Signature verfiy pass\n"
     ]
    }
   ],
   "source": [
    "root_cert = load_certificate(FILETYPE_PEM, open('root.crt').read())\n",
    "client_cert = load_certificate(FILETYPE_PEM, metadata['Cert'])\n",
    "store = X509Store()\n",
    "store.add_cert(root_cert)\n",
    "store_ctx = X509StoreContext(store, client_cert)\n",
    "try:\n",
    "    if store_ctx.verify_certificate()==None:\n",
    "        print('Device certificate verify pass')\n",
    "except X509StoreContextError:\n",
    "    print('Device certificate verfiy fail')\n",
    "\n",
    "signature = metadata['Signature']\n",
    "del metadata['Signature']\n",
    "message = pickle.dumps(metadata)\n",
    "public_key = x509.load_pem_x509_certificate(bytes(metadata['Cert'], encoding='utf-8')).public_key()\n",
    "try:\n",
    "    public_key.verify(\n",
    "        signature,\n",
    "        message,\n",
    "        padding.PSS(\n",
    "            mgf=padding.MGF1(hashes.SHA256()),\n",
    "            salt_length=padding.PSS.MAX_LENGTH\n",
    "        ),\n",
    "        hashes.SHA256()\n",
    "    )\n",
    "    print('Signature verfiy pass')\n",
    "except:\n",
    "    print('Signature verfiy fail')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>6.Call the contract provide the public key to get the encrypted password</i></b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Before call the contract:\n",
      "Vehicle account balance:107.99171151092\n",
      "Consumer account balance:76.998461915\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "HexBytes('0x61db279b03a35a3b03aff94969d59051e725b8cc8f3640d41464ac5779a40d6d')"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "contract_instance = w3.eth.contract(address=contract_address, abi=contract_interface['abi'])\n",
    "result_filter = w3.eth.filter({'fromBlock':0, 'address':contract_address})\n",
    "print('Before call the contract:')\n",
    "print('Vehicle account balance:{}'.format(Web3.fromWei(w3.eth.get_balance(w3.eth.accounts[1]), 'ether')))\n",
    "print('Consumer account balance:{}'.format(Web3.fromWei(w3.eth.get_balance(w3.eth.accounts[2]), 'ether')))\n",
    "w3.eth.default_account=w3.eth.accounts[2]\n",
    "w3.geth.personal.unlockAccount(w3.eth.accounts[2], 'abc12345')\n",
    "contract_instance.functions.deposit(open('my_pubkey.pem', 'r').read()).transact(transaction={'value':Web3.toWei(1, 'ether'), 'gas': 2100000})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>7.Monitor the contract event to get the encrypted password and decrypt it with own private key</i></b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Identifier:0x393b9aeab28aa2dd4faf840a00f0787a9b7a0519\n",
      "Encrypted password:bT/nooqOqV0fBPvHn1Qs9UgUH2RJTLP+rFIGo6Ff8Nu2LVhDBE7gZDOrYdAhAF3vQpEAJ3fqIW9HdpEyEvkC1QocGEQDV5JnENfaOh7rrZzeIC0KudBevSp07YwjO97e75853w6XVZ3pFBANtLteskKI0Gc1vTMo5CPPKJt4xi8=\n",
      "Decrypted password:b'abcde123456'\n"
     ]
    }
   ],
   "source": [
    "while True:\n",
    "    events = result_filter.get_new_entries()\n",
    "    if len(events)==0:\n",
    "        time.sleep(2)\n",
    "        continue\n",
    "    else:\n",
    "        data = events[0]['data'][2:]\n",
    "        identifier = Web3.toHex(hexstr=data[88:128])\n",
    "        encrypted_len = Web3.toInt(hexstr=data[192:256])*2\n",
    "        encrypted = Web3.toText(hexstr=data[256:(256+encrypted_len)])\n",
    "        with open('my_prikey.pem', 'r') as f:\n",
    "            private_pem = f.read()\n",
    "        pr_key = RSA.import_key(private_pem)\n",
    "        decrypt = PKCS1_OAEP.new(key=pr_key)\n",
    "        decrypted_password = decrypt.decrypt(base64.b64decode(encrypted))\n",
    "        print('Identifier:{}\\nEncrypted password:{}\\nDecrypted password:{}'.format(identifier, encrypted, decrypted_password))\n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>8.Check the account balance after the contract executed.</i></b><br>\n",
    "Vehicle account should get 1 ether, consumer account should pay 1 ether."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Vehicle account balance:108.99165827691\n",
      "Consumer account balance:75.998357363\n"
     ]
    }
   ],
   "source": [
    "print('Vehicle account balance:{}'.format(Web3.fromWei(w3.eth.get_balance(w3.eth.accounts[1]), 'ether')))\n",
    "print('Consumer account balance:{}'.format(Web3.fromWei(w3.eth.get_balance(w3.eth.accounts[2]), 'ether')))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>9.Rate the data</i></b><br>\n",
    "Call the contract rate function to give a score. Only the account that previous buyed the data file could rate."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "HexBytes('0xe42e67a2bb02c62bee96151b7c9ca88640fe39d54a46ad702cb6bc44d7f07ea3')"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "w3.eth.default_account=w3.eth.accounts[2]\n",
    "w3.geth.personal.unlockAccount(w3.eth.accounts[2], 'abc12345')\n",
    "contract_instance.functions.rate(5).transact()    #Rate score 5"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<b><i>10.Check the rating for the data file</i></b><br>\n",
    "Check the contract event with the topic LogRating and extract the value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Buyer address:0x393b9aeab28aa2dd4faf840a00f0787a9b7a0519, rating score:5\n"
     ]
    }
   ],
   "source": [
    "f = w3.eth.filter({'fromBlock': 0, 'toBlock': 'latest'})\n",
    "entries = f.get_all_entries()\n",
    "ratings = []\n",
    "for entry in entries:\n",
    "    if entry['address']==contract_address and entry['topics'][0]==Web3.keccak(text='LogRating((address,uint256))'):\n",
    "        data = entry['data'][2:]\n",
    "        buyer_addr = Web3.toHex(hexstr=data[24:64])\n",
    "        score = Web3.toInt(hexstr=data[64:128])\n",
    "        ratings.append((buyer_addr, score))\n",
    "        print(\"Buyer address:{}, rating score:{}\".format(buyer_addr, score))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "w3.geth.miner.stop()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
